
package com.zcp.pcsuitall;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import org.xmlpull.v1.XmlSerializer;

import android.util.Log;
import android.util.Xml;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.LinkedList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * XML 数据处理。
 */
public class XmlDataDealWith {

    private static final String SPACE="";
    private static final String DATA="data";
    private static final String ROOT="root";
    private static final String UTF_8="utf-8";
    private static final String ACTION="action";
    private static final String FROM="from";
    private static final String TO="to";
    
    /**
     * 文件能正常被xml解析。<br>
     * 
     * @param value
     * @return
     */
    public static boolean checkCanBeenParse(String value) {
//        createFileDetailXml("/sdcard/test.xml",value);
        boolean isCan = false;
        StringReader stringRead=new StringReader(value);
        XmlPullParser parser =Xml.newPullParser(); 
        try {
            parser.setInput(stringRead);
            
            int event = parser.getEventType();// 产生第一个事件
            while (event != XmlPullParser.END_DOCUMENT) {
//                switch (event) {
//                    case XmlPullParser.START_TAG:// 判断当前事件是否是标签元素开始事件
////                        Log.e("aa begin", parser.getName());
//                        break;
//                    case XmlPullParser.TEXT:
////                        Log.e("aa value", parser.getText());
//                        break;
//                    case XmlPullParser.END_TAG:
////                        Log.e("aa end", parser.getName());
//                        break;
//                }
                event = parser.next();// 进入下一个元素并触发相应事件
            }// end while
            isCan = true;
        } catch (Exception e) {
//            Log.e("aa", "error "+e.toString());
            e.printStackTrace();
            return false;
        }finally{
            try{
                parser=null;
                if(stringRead!=null)
                {
                    stringRead.close();
                    stringRead=null;
                }
            }catch(Exception e)
            {
                
            }
        }
        return isCan;
    }
    
    /**
     * 文件能正常被xml解析。<br>
     * 
     * @param value
     * @return
     */
    public static String getFormatByXml(String value) {
        StringBuffer format=new StringBuffer("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
        StringReader stringRead=new StringReader(value);
        XmlPullParser parser =Xml.newPullParser(); 
        try {
            parser.setInput(stringRead);
            int event = parser.getEventType();// 产生第一个事件
            while (event != XmlPullParser.END_DOCUMENT) {
                switch (event) {
                    case XmlPullParser.START_TAG:// 判断当前事件是否是标签元素开始事件
                        format.append("<"+parser.getName()+">");
//                        Log.e("aa begin", parser.getName());
                        break;
                    case XmlPullParser.TEXT:
//                        Log.e("aa value", parser.getText());
                        break;
                    case XmlPullParser.END_TAG:
                         format.append("</"+parser.getName()+">");
                        break;
                }
                event = parser.next();// 进入下一个元素并触发相应事件
            }
        } catch (Exception e) {
            e.printStackTrace();
            return format.toString();
        }finally{
            try{
                parser=null;
                if(stringRead!=null)
                {
                    stringRead.close();
                    stringRead=null;
                }
            }catch(Exception e)
            {
                
            }
        }
        return format.toString();
    }
    
    
    
    /**
     * 获取action。<br>
     * 
     * @param value
     * @return 如果xml语法通不过，设置name为formatError。
     */
    public static Action getActionValue(String value) {
        Action action = new Action(null, null);
        action.setSource(value);
        StringReader stringRead = new StringReader(value);
        XmlPullParser parser = Xml.newPullParser();
        try {
            parser.setInput(stringRead);
            int event = parser.getEventType();// 产生第一个事件
            while (event != XmlPullParser.END_DOCUMENT) {
                switch (event) {
                    case XmlPullParser.START_TAG:// 判断当前事件是否是标签元素开始事件
                        if (parser.getDepth() == 2) {
                            if (ACTION.equals(parser.getName())) {
                                action.setName(parser.nextText());
                            } else if (FROM.equals(parser.getName())) {
                                action.setFrom(parser.nextText());
                            } else if (TO.equals(parser.getName())) {
                                action.setTo(parser.nextText());
                            }
                        }
                        break;
                }
                event = parser.next();// 进入下一个元素并触发相应事件
            }
        } catch (Exception e) {
            action.setName("formatError");
            e.printStackTrace();
        } finally {
            try {
                parser = null;
                if (stringRead != null) {
                    stringRead.close();
                    stringRead = null;
                }
            } catch (Exception e) {

            }
        }
        return action;
    }
    
    
    /**
     * 获取元素的数量。<br>
     * 
     * @param value
     * @return
     */
    public static int getElementNumber(String value, String element, int depth) {
        int number = 0;
        StringReader stringRead = new StringReader(value);
        XmlPullParser parser = Xml.newPullParser();
        try {

            
            parser.setInput(stringRead);

            int event = parser.getEventType();// 产生第一个事件
            while (event != XmlPullParser.END_DOCUMENT) {
                switch (event) {
                    case XmlPullParser.START_TAG:// 判断当前事件是否是标签元素开始事件
                        if (element.equals(parser.getName()) && parser.getDepth() == depth) {
                            number++;
                        }
                        break;
                }
                event = parser.next();// 进入下一个元素并触发相应事件
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                parser=null;
                if (stringRead != null) {
                    stringRead.close();
                    stringRead=null;
                }
            } catch (Exception e) {

            }
        }
        return number;
    }
    
    
    
    public static void getAllXML(String value) throws XmlPullParserException, IOException {
        {
            XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
            factory.setNamespaceAware(true);
            XmlPullParser parser = factory.newPullParser();
            InputStream inputStream = new ByteArrayInputStream(value.getBytes());
            parser.setInput(new InputStreamReader(inputStream));
            int eventType = parser.getEventType();
            do {
                // XmlUtils.nextElement(parser);
                parser.next();
                eventType = parser.getEventType();
                if (eventType == XmlPullParser.TEXT) {
                    Log.d("test", parser.getText());
                }
            } while (eventType != XmlPullParser.END_DOCUMENT);
        }
    }

    
    /**
     * 生成xml文件(Tested)。<br>
     * 如果要使用这个方法，并且在sd卡下面写数据，需要配置权限。
     * @param fileName 要生成文件名(全路径)。<br>
     * @param fileValue 文件的内容。<br>
     * @return
     */
    public static boolean createFileDetailXml(String fileName, String fileValue) {
//        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
//        <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
        OutputStream os = null;
        OutputStreamWriter osw = null;
        try {
            File file=new File(fileName);
            if(!file.exists())
            {
                file.createNewFile();
            }
            os = new FileOutputStream(file, true);
            osw = new OutputStreamWriter(os);
            osw.write(fileValue);
        } catch (FileNotFoundException e) {
            return false;
        } catch (IOException e) {
            return false;
        } finally {
            if (osw != null) {
                try {
                    osw.close();
                } catch (IOException e) {
                }
            }
            if (os != null) {
                try {
                    os.close();
                } catch (IOException e) {
                }
            }
        }
        return true;
    }
    
    
    /**
     * 获取xml数据的格式，就是去掉数据。 比如数据
     * "<?xml version='1.0' encoding='utf-8'?><root><action>LoadSystemInfo</action><data><type>sdk</type><device_id>000000000000000</device_id></data></root>"
     * 的格式就是
     * "<?xml version='1.0' encoding='utf-8'?><root><action></action><data><type></type><device_id></device_id></data></root>"
     * 
     * @param source
     * @return
     */
    public static String getXmlFormat(String source) {
        if(source==null)
        {
            return null;
        }
        
        return getFormatByXml(source);
        //不使用正则表达式获取fromat
//        source=source.replaceAll(">\\s*?\r*?\n*?\u0085*?\u2028*?\u2029*?\\s*?<", "><");//去换行符处理
//        StringBuilder result = new StringBuilder();
//        Pattern p = Pattern.compile(">.*?<");
//        String[] arrays = p.split(source);
//
//        for (int i = 0; i < arrays.length; i++) {
//            if (i != 0) {
//                result.append("<");
//            }
//            result.append(arrays[i]);
//            if (i != arrays.length - 1) {
//                result.append(">");
//            }
//        }
//        return result.toString();
    }
    
    /**
     * 注意不支持大数据量操作避免出现内存溢出。
     * 解析xml格式数据，source =
     * "<?xml version=\"1.0\" encoding=\"utf-8\"?><root><action>AppManager</action><data><item><id></id><name>suchangjun</name></item><item><id>xj</id><name>xuexuejian</name></item></data></root>"
     * 。<br>
     * 就是把item中数据解析出来。比如上面就可以解析两个item actionName 都是AppManager,如果keys是{id,name}就会有id和name的数据。
     * @param keys item下面没一项的要解析的key。
     * @param source 要被解析的数据。
     * @return Item链表数据。
     */
    public static LinkedList<Item> getData(String[] keys, String source) {
        return getData(keys,source,"item");
    }
    
    /**
     * 获取item 下面的元素数据，注意item已经没有递归嵌套,action的元素排在item前面。
     * 
     * @param keys
     * @param source
     * @param ItemDepth item 所在的层次，root为第0层。
     * @param actionDepth action 所在的层次，root为第0层。
     * @return
     */
    public static LinkedList<Item> getDataByXml(String[] keys, String source, int ItemDepth,
            int actionDepth) {
        LinkedList<Item> result = new LinkedList<Item>();
        StringReader stringRead = new StringReader(source);
        XmlPullParser parser = Xml.newPullParser();
        try {
            parser.setInput(stringRead);
            Item item = null;
            String action = null;
            int keyDepth = ItemDepth + 1;
            int event = parser.getEventType();// 产生第一个事件
            while (event != XmlPullParser.END_DOCUMENT) {
                switch (event) {
                    case XmlPullParser.START_TAG:// 判断当前事件是否是标签元素开始事件
                    {
                        if ("action".equals(parser.getName()) && parser.getDepth() == actionDepth) {
                            action = parser.nextText();
                        } else if ("item".equals(parser.getName())
                                && parser.getDepth() == ItemDepth) {
                            item = new Item();
                        } else if (parser.getDepth() == keyDepth) {
                            for (String key : keys) {
                                if (key.equals(parser.getName())) {
                                    item.setItemData(key, parser.nextText());
                                    break;
                                }
                            }
                        }
                        break;
                    }
                    case XmlPullParser.END_TAG: {
                        if ("item".equals(parser.getName()) && parser.getDepth() == ItemDepth) {
                            item.setActionName(action);
                            result.add(item);
                            item = null;
                            break;
                        }
                    }
                }
                event = parser.next();// 进入下一个元素并触发相应事件
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (stringRead != null) {
                    stringRead.close();
                    stringRead=null;
                }
                parser=null;
            } catch (Exception e) {

            }
        }
        return result;
    }
    
    /**
     * 解析xml格式数据，source =
     * "<?xml version=\"1.0\" encoding=\"utf-8\"?><root><action>AppManager</action><data><item><id></id><name>suchangjun</name></item><item><id>xj</id><name>xuexuejian</name></item></data></root>"
     * 。<br>
     * 就是把childrenkey(本例它是item)中数据解析出来。比如上面就可以解析两个childrenkey actionName 都是AppManager,如果keys是{id,name}就会有id和name的数据。
     * @param keys item下面没一项的要解析的key。
     * @param source 要被解析的数据。
     * @param childrenkey 要被解析的数据循环key。
     * @return Item链表数据。
     */
    public static LinkedList<Item> getData(String[] keys, String source,String childrenkey) {
        LinkedList<Item> result = new LinkedList<Item>();
        source=source.replaceAll(">\\s*?\r*?\n*?\u0085*?\u2028*?\u2029*?\\s*?<", "><");//去换行符处理
        String action = getRegularSingle("action", source,false);
        LinkedList<String> itemList = getRegular(childrenkey, source,false);
        Item item = null;
        for (String element : itemList) {
            item = new Item();
            item.setActionName(action);
            for (String key : keys) {
                item.setItemData(key, getRegularSingleAnyDealWith(key, element));
            }
            result.add(item);
        }
        return result;
    }
        
    /**
     * 获取正则表达式数据，比如regularKey是action,那么就是获取<action>AppManager</action><action>hi
     * </action> 中的AppManager和hi，获取所有的数据。
     * 
     * @param regularKey 正则表达式的key。
     * @param source 数据源。
     * @return 如果没有这个数据那么就返回空链表。
     */
    public static LinkedList<String> getRegular(String regularKey, String source) {
        return getRegular(regularKey, source,false);
    }

    /**
     * 获取正则表达式数据，比如regularKey是action,那么就是获取<action>AppManager</action><action>hi
     * </action> 中的AppManager和hi，获取所有的数据。
     * 
     * @param regularKey 正则表达式的key。
     * @param source 数据源。
     * @return 如果没有这个数据那么就返回空链表。
     */
    public static LinkedList<String> getRegular(String regularKey, String source,boolean replay) {
        LinkedList<String> result = new LinkedList<String>();
        if (replay) {
            source = source.replaceAll(">\\s*?\r*?\n*?\u0085*?\u2028*?\u2029*?\\s*?<", "><");// 去换行符处理
        }
        Pattern p = Pattern.compile("(?<=<" + regularKey + ">)(.*?[\r*?\n*?\u0085*?\u2028*?\u2029*?]*?.*?)(?=</" + regularKey + ">)");
        Matcher m = p.matcher(source);
        while (m.find()) {
            result.add(m.group(0));
        }
        return result;
    }
    
    
    /**
     * 获取正则表达式数据，比如regularKey是action,那么就是获取<action>AppManager</action><action>hi
     * </action> 中的AppManager和hi，获取所有的数据。
     * 
     * @param regularKey 正则表达式的key。
     * @param source 数据源。
     * @return 如果没有这个数据那么就返回空链表。
     */
    public static LinkedList<String> getRegular(String regularKeyBegin,String regularKeyEnd,String source,boolean replay) {
        LinkedList<String> result = new LinkedList<String>();
        if (replay) {
            source = source.replaceAll(">\\s*?\r*?\n*?\u0085*?\u2028*?\u2029*?\\s*?<", "><");// 去换行符处理
        }
        Pattern p = Pattern.compile("(?<=<" + regularKeyBegin + ">)(.*?[\r*?\n*?\u0085*?\u2028*?\u2029*?]*?.*?)(?=</" + regularKeyEnd + ">)");
        Matcher m = p.matcher(source);
        while (m.find()) {
            result.add(m.group(0));
        }
        return result;
    }

    
    /**
     * 获取正则表达式数据，比如regularKey是action,那么就是获取<action>AppManager</action>
     * 中的AppManager，只是获取第一个如果有多余就不获取。
     * 
     * @param regularKey 正则表达式的key。
     * @param source 数据源。
     * @return 如果没有这个数据那么就返回null
     */
    public static String getRegularSingle(String regularKey, String source) {
        return getRegularSingle(regularKey,source,true);
    }
    
    /**
     * 获取正则表达式数据，比如regularKey是action,那么就是获取<action>AppManager</action>
     * 中的AppManager，只是获取第一个如果有多余就不获取。
     * 
     * @param regularKey 正则表达式的key。
     * @param source 数据源。
     * @return 如果没有这个数据那么就返回null
     */
    public static String getRegularSingle(String regularKey, String source,boolean replay) {
        String result = null;
        if (replay) {
            source = source.replaceAll(">\\s*?\r*?\n*?\u0085*?\u2028*?\u2029*?\\s*?<", "><");// 去换行符处理
        }
        Pattern p = Pattern.compile("(?<=<" + regularKey + ">)(.*?[\r*?\n*?\u0085*?\u2028*?\u2029*?]*?.*?)(?=</" + regularKey + ">)");
        Matcher m = p.matcher(source);
        if (m.find()) {
            result = m.group(0);
        }
        return result;
    }
    
    
    /**
     * 获取正则表达式数据，比如regularKey是action,那么就是获取<action>AppManager</action>
     * 中的AppManager，只是获取第一个如果有多余就不获取。
     * 
     * @param regularKey 正则表达式的key。
     * @param source 数据源。
     * @return 如果没有这个数据那么就返回null
     */
    public static String getRegularSingleAnyDealWith(String regularKey, String source) {
        Pattern p = Pattern.compile("(?<=<" + regularKey + ">)(.*?[\r*?\n*?\u0085*?\u2028*?\u2029*?]*?.*?)(?=</" + regularKey + ">)");
        Matcher m = p.matcher(source);
        if (m.find()) {
            return  m.group(0);
        }
       return null;
    }    
    
//    /**
//     * 解析通道中数据为action。<br>
//     * 
//     * @param sc 通道，必须支持可读。<br>
//     */
//    public static byte[] analysisDataToByte(SocketChannel sc) {
//        byte[] value = null;
//        try {
//            ByteArrayOutputStream bos = new ByteArrayOutputStream(1024);
//            ByteBuffer r_buff = ByteBuffer.allocate(1024);
//
//            int size = 0;
//            r_buff.clear();
//            while ((size = sc.read(r_buff)) > 0) {
//                /** 反转此缓冲区。首先将限制设置为当前位置，然后将位置设置为 0,保证可读取。 **/
//                r_buff.flip();
//                bos.write(r_buff.array(), 0, size);
//                bos.flush();
//                r_buff.clear();
//            }
//
//            value = bos.toByteArray();
//            bos.close();
//
//        } catch (IOException e) {
//            Log.e(TAG, "analysisDataToByte");
//            e.printStackTrace();
//        }
//        return value;
//    }
    
    private static final String ENDTAG="</root>";
    /**
     * 把通道中数据读取到文件流中(如果文件太长，会分几次发送，所以这边要增加处理)。
     * 1：修改解决分包读取问题。
     * 2：解决结束标识符号</root>被分开读取问题。
     * 2：由于缓存中文被分开读取问题。
     * @param sc
     * @param cipherKey 密钥.
     * @param toFile
     */
    public static String getDataFromChannel(SocketChannel sc,String cipherKey) {
//        Log.i("222", "getDataFromChannel"+cipherKey);
        ByteBuffer r_buff = ByteBuffer.allocate(1024);
        ByteArrayOutputStream byteOut=new ByteArrayOutputStream();
        try {
            r_buff.clear();
            int size = 0;
            boolean close = false;
            while (!close) {
                while ((size = sc.read(r_buff)) > 0) {
                    // 确保r_buff可读
                    r_buff.flip();
                    byteOut.write(r_buff.array(), 0, size);
                    //</root> 有可能会被分开读取（由于缓存大小限制是分开读取），所以需要增加判断。
                    byteOut.flush();
                    //由于有可能密文不全在解密的时候会抛出异常。
                    try {
                        if ((MyDes.decryptDES(byteOut.toString(UTF_8), cipherKey).indexOf(ENDTAG) != -1)) {
                            close = true;
                            r_buff.clear();
                            break;
                        }
                    } catch (Exception e) {
//                        e.printStackTrace();
                    }
                    r_buff.clear();
                }
            }
            close = false;

        } catch (Exception e) {
            e.printStackTrace();
        }
        String result=null;
        try {
            byteOut.flush();
            result = MyDes.decryptDES(byteOut.toString(UTF_8),cipherKey);
            byteOut.close();
            byteOut=null;
            r_buff=null;
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
        return result;

    }
    
    /**
     * 返回系统类错误消息。
     * 
     * @param cmd
     * @param message
     * @return
     */
    public static String getSystemErrorString(String cmd, String message) {
        return XmlDataDealWith.getErrorString(cmd, "1000", message);
    }

    /**
     * 返回错误格式。
     * 
     * @param cmd 本次命令调用。
     * @param code 如果不是业务错误就是1000，其它就是业务错误码。
     * @param message 调试信息。
     * @return
     */
    public static String getErrorString(String cmd, String code, String message) {
        StringBuilder returnMessage = new StringBuilder();
        returnMessage.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
        returnMessage.append("<root><action>Error</action><data>");
        returnMessage.append("<cmd>" + cmd + "</cmd>");
        returnMessage.append("<code>" + code + "</code>");
        returnMessage.append("<message>" + message + "</message>");
        returnMessage.append("</data></root>");
        return returnMessage.toString();
    }

    /**
     * 生成前缀。
     * 
     * @param action
     * @param serializer
     * @throws IOException
     * @throws IllegalStateException
     * @throws IllegalArgumentException
     */
    public static void createPre(Action action, XmlSerializer serializer)
            throws IllegalArgumentException, IllegalStateException, IOException {
        serializer.startDocument(UTF_8, true);
        // 第一个参数为命名空间,如果不使用命名空间,可以设置为null
        serializer.startTag(SPACE, ROOT);
        serializer.startTag(SPACE, ACTION);
        serializer.text(action.getName());
        serializer.endTag(SPACE, ACTION);
        serializer.startTag(SPACE, DATA);
    }

//    /**
//     * 解析通道中数据为action。<br>
//     * 
//     * @param sc 通道，必须支持可读。<br>
//     */
//    public static Action analysisData(SocketChannel sc) {
//        Action action = null;
//        byte[] value = analysisDataToByte(sc);
//        if (value != null) {
//            InputStream inputStream = new ByteArrayInputStream(value);
//            action = analysisInputStream(inputStream);
//            Log.i(TAG, "writeDataToAction [" + action.getName() + "] [" + action.getValue() + "]");
//        }
//        return action;
//    }
//
//    /**
//     * 把byte[] 中数据提取action.
//     * 
//     * @param value
//     * @return
//     */
//    public static Action analysisData(byte[] value) {
//        Action action = null;
//        if (value != null) {
//            InputStream inputStream = new ByteArrayInputStream(value);
//            action = analysisInputStream(inputStream);
//            Log.i(TAG, "writeDataToAction [" + action.getName() + "] [" + action.getValue() + "]");
//        }
//        return action;
//    }



//    /**
//     * 分析文件流。<br>
//     * 
//     * @param inputStream
//     * @return
//     */
//    private static Action analysisInputStream(InputStream inputStream) {
//        Action action = null;
//        try {
//            XmlPullParser parser = Xml.newPullParser();
//            parser.setInput(inputStream, "UTF-8");
//            int event = parser.getEventType();// 产生第一个事件
//            while (event != XmlPullParser.END_DOCUMENT) {
//                switch (event) {
//                    case XmlPullParser.START_DOCUMENT:// 判断当前事件是否是文档开始事件
//                        action = new Action(null, null, null);// 初始化action集合
//                        break;
//                    case XmlPullParser.START_TAG:// 判断当前事件是否是标签元素开始事件
//                        if ("action".equals(parser.getName())) {// 判断开始标签元素是否是action
//                            action.setName(parser.getAttributeValue("", "name"));
//                            action.setValue(parser.getAttributeValue("", "value"));
//                            action.setOperate(parser.getAttributeValue("", "operate"));
//                        }
//                        break;
//                }
//                event = parser.next();// 进入下一个元素并触发相应事件
//            }// end while
//
//        } catch (Exception e) {
//            Log.e(TAG, e.getMessage());
//        }
//        return action;
//    }

    private static String TAG = "XmlDataDealWith";
}
